#include "fssimulationcasepersistenceassistant.h"

#include "../documents/data/connectionserializationhelper.h"
#include "../documents/data/simgraphicdata.h"
#include "../documents/swpsimulationcasedocument.h"
#include "../utils/editoritemfactory.h"
#include "fspersistencecommon.h"
#include "fssimulationcasepersistentdocument.h"

#include <icontainerdocument.h>
#include <ieditoritem.h>
#include <ilinkeddocument.h>
#include <istorage.h>
#include <persistence.h>
#include <serializationengine.h>

#include <QtCore/QDataStream>
#include <QtCore/QUrl>

using namespace core;
using namespace fspersistence;
using namespace swp;

typedef QSharedPointer<IDocument> Document;

FsSimulationCasePersistenceAssistant::FsSimulationCasePersistenceAssistant(
    const MimeType& mimeType) : IPersistenceAssistant(),
        m_mimeType(mimeType),
        m_persistentDocument(),
        m_document()
{

}

FsSimulationCasePersistenceAssistant::~FsSimulationCasePersistenceAssistant()
{

}

// IPersistenceAssistant
bool FsSimulationCasePersistenceAssistant::canOpen(
    const MimeType& mimeType) const
{
    return mimeType == m_mimeType;
}

bool FsSimulationCasePersistenceAssistant::canSave(
    const MimeType& mimeType) const
{
    return mimeType == m_mimeType;
}

bool FsSimulationCasePersistenceAssistant::isNative(
    const QSharedPointer<IPersistentDocument>& document) const
{
    return document->mimeType() == m_mimeType;
}

QString FsSimulationCasePersistenceAssistant::forStorage() const
{
    return FILE_SYSTEM_STORAGE;
}

bool FsSimulationCasePersistenceAssistant::isNative(
    const MimeType& mimeType) const
{
    return mimeType == m_mimeType;
}

QSharedPointer<IDocument> FsSimulationCasePersistenceAssistant::open()
{
    Q_ASSERT(!m_persistentDocument.isNull());

    const QSharedPointer<FsSimulationCasePersistentDocument> _document =
        qSharedPointerDynamicCast<FsSimulationCasePersistentDocument>(
            m_persistentDocument);

    Q_ASSERT(!_document.isNull());

    const IStorage* storage = _document->storage();
    const QByteArray data = storage->read(m_persistentDocument);
    _document->updateData(data);

    QDataStream stream(data);
    stream.setVersion(QDataStream::Qt_4_7);

    QString name;

    stream >> name;

    if (m_document.isNull())
    {
        m_document = QSharedPointer<IDocument>(new SwpSimulationCaseDocument(
            name, _document->mimeType(),
            QSharedPointer<IContainerDocument>(0), true));
    }
    else
    {
        m_document->rename(name);
    }

    bool isValid = false;

    stream >> isValid;

    m_document->setValid(isValid);

    m_document->setFuture(false);

    SerializationEngine* serializer = new SerializationEngine();

    SimGraphicData* gData =  dynamic_cast<SimGraphicData* >(m_document->data());

    Q_ASSERT(gData != 0);

    QString serializedPalette;
    stream >> serializedPalette;

    Palette* palette = Palette::defaultPalette();

    bool correct = serializer->deserialize(serializedPalette,
        palette);

    if (correct)
    {
        gData->setGraphicPalette(palette);
    }

    delete palette;

    QRectF sceneRect;

    stream >> sceneRect;
    gData->setSceneRect(sceneRect);

    int itemCount = -1;

    stream >> itemCount;

    gData->setIgnoreSnapFlag(true);

    for (int i = 0; i < itemCount; ++i)
    {
        int type;
        QString serializedItem;

        stream >> type >> serializedItem;

        IGraphicEditorItem* item = dynamic_cast<IGraphicEditorItem*>(
            EditorItemFactory::getInstance()->editorItem(type));

        Q_ASSERT(item != 0); // type mismatch or not registered

        item->setParentDocumentData(gData);

        const bool initiated = serializer->deserialize(serializedItem, item);

        if (initiated)
        {
            gData->addGraphicItem(item);
        }
        else
        {
            delete item;
        }
    }

    int connectionCount = -1;

    stream >> connectionCount;

    for (int i = 0; i < connectionCount; ++i)
    {
        QString serializedConnection;

        stream >> serializedConnection;

        ConnectionSerializationHelper helper(gData, 0);

        serializer->deserialize(serializedConnection,
            &helper);
    }

    QList<QUuid> simPath;

    stream >> simPath;

    gData->tryToCreateSimPath(simPath);

    gData->setIgnoreSnapFlag(false);

    m_document->setDirty(false);

    delete serializer;

    return m_document;
}

QSharedPointer<IDocument> FsSimulationCasePersistenceAssistant::openFuture()
{
    Q_ASSERT(!m_persistentDocument.isNull());

    const QSharedPointer<FsSimulationCasePersistentDocument> _document =
        qSharedPointerDynamicCast<FsSimulationCasePersistentDocument>(
            m_persistentDocument);

    Q_ASSERT(!_document.isNull());

    const IStorage* storage = _document->storage();
    const QByteArray data = storage->read(m_persistentDocument);
    _document->updateData(data);

    QDataStream stream(data);
    stream.setVersion(QDataStream::Qt_4_7);

    QString name;

    stream >> name;

    bool isValid = false;

    stream >> isValid;

    m_document = QSharedPointer<IDocument>(new SwpSimulationCaseDocument(name,
        _document->mimeType(),
        QSharedPointer<IContainerDocument>(0), true));

    m_document->setValid(isValid);
    m_document->setDirty(false);

    return m_document;
}

QSharedPointer<IPersistentDocument>
    FsSimulationCasePersistenceAssistant::save()
{
    Q_ASSERT(!m_document.isNull());

    QByteArray data;

    QDataStream stream(&data, QIODevice::WriteOnly);
    stream.setVersion(QDataStream::Qt_4_7);

    stream << m_document->name();

    stream << m_document->isValid();

    SerializationEngine* serializer = new SerializationEngine();

    SimGraphicData* gData =  dynamic_cast<SimGraphicData* >(
        m_document->data());

    Q_ASSERT(gData != 0);

    bool serializedProperly2 = true;
    const QString serializedPalette =
        serializer->serialize(gData->graphicPalette(), &serializedProperly2);

    stream << serializedPalette;

    stream << gData->sceneRect();

    const QList<IGraphicEditorItem* > items = gData->graphicItems();

    const int itemCount = items.size();

    stream << itemCount;

    bool serializedProperly = true;

    for (int i = itemCount - 1; i >= 0; --i)
    {
        IGraphicEditorItem* item = items[i];
        const QString serializedItem = serializer->serialize(item,
            &serializedProperly);

        stream << item->type() << serializedItem;

        if (!serializedProperly)
        {
            Q_ASSERT(false); // should always pass
            break;
        }
    }

    const QList<ConnectionData> connections = gData->connections();

    const int connectionCount = connections.size();

    stream << connectionCount;

    bool serializedProperly1 = true;

    for (int i = 0; i < connectionCount; ++i)
    {
        ConnectionSerializationHelper helper(gData, &connections[i]);

        const QString serializedConnection = serializer->serialize(&helper,
            &serializedProperly1);

        stream << serializedConnection;

        if (!serializedProperly1)
        {
            Q_ASSERT(false); // should always pass
            break;
        }
    }

    stream << gData->simPath();

    if (serializedProperly && serializedProperly1 && serializedProperly2)
    {
        m_persistentDocument->updateData(data);
        m_persistentDocument->storage()->write(m_persistentDocument);
    }

    delete serializer;

    return m_persistentDocument;
}

void FsSimulationCasePersistenceAssistant::closeAndKeepFuture()
{
    Q_ASSERT(!m_document.isNull());

    GraphicData* gData =  dynamic_cast<GraphicData* >(m_document->data());

    Q_ASSERT(gData != 0);

    gData->clearData();

    m_document->setDirty(false);
    m_document->setFuture();
}

bool FsSimulationCasePersistenceAssistant::createPersistentDocument(
    const QUrl& url, const IStorage* storage)
{
    bool ret = false;

    QFileInfo info(url.toLocalFile());

    if (info.exists())
    {
        QSharedPointer<IPersistentDocument> document(
            new FsSimulationCasePersistentDocument(info, storage));

        m_persistentDocument = document;

        ret = true;
    }

    return ret;
}

QSharedPointer<IDocument>
    FsSimulationCasePersistenceAssistant::getDocument() const
{
    return m_document;
}

QSharedPointer<IPersistentDocument> FsSimulationCasePersistenceAssistant::
    getPersistentDocument() const
{
    return m_persistentDocument;
}

IPersistenceAssistant* FsSimulationCasePersistenceAssistant::copy() const
{
    FsSimulationCasePersistenceAssistant* ret =
        new FsSimulationCasePersistenceAssistant(m_mimeType);

    return ret;
}

void FsSimulationCasePersistenceAssistant::setDocument(
    const QSharedPointer<IDocument>& document)
{
    if (m_document.isNull())
    {
        m_document = document;
    }
    else
    {
        Q_ASSERT(false); // document is already set
    }
}

void FsSimulationCasePersistenceAssistant::setPersistentDocument(
    const QSharedPointer<IPersistentDocument>& document)
{
    if (m_persistentDocument.isNull())
    {
        m_persistentDocument = document;
    }
    else
    {
        Q_ASSERT(false); // document is already set
    }
}

QSharedPointer<IPersistenceAssistant>
    FsSimulationCasePersistenceAssistant::getNativeAssistant(
        const QSharedPointer<IDocument>& document) const
{
    QList<QSharedPointer<IPersistenceAssistant> > assistants =
        Persistence::getNativeAssistants(document);

    QSharedPointer<IPersistenceAssistant> assistant;

    foreach (const QSharedPointer<IPersistenceAssistant>& _assistant,
        assistants)
    {
        if (!_assistant->getPersistentDocument().isNull())
        {
            assistant = _assistant;
            break;
        }
        else
        {
            Persistence::closeAssistant(_assistant);
        }
    }

    return assistant;
}

void FsSimulationCasePersistenceAssistant::findAndAddLinks(
    const QStringList& linkUrls)
{
    const QSharedPointer<ILinkedDocument> linkedDocument =
        qSharedPointerDynamicCast<ILinkedDocument>(m_document);

    const QSharedPointer<IContainerDocument> container =
        m_document->container();

    const QStringList categories = container->categories();

    foreach (const QString& linkUrl, linkUrls)
    {
        const QUrl _linkUrl = QUrl::fromLocalFile(linkUrl);

        foreach (const QString& category, categories)
        {
            const QList<Document> documents = container->documents(category);

            foreach (const Document& document, documents)
            {
                const QSharedPointer<IPersistenceAssistant> assistant =
                    getNativeAssistant(document);

                if (!assistant.isNull() &&
                    !assistant->getPersistentDocument().isNull() &&
                    assistant->getPersistentDocument()->url() == _linkUrl)
                {
                    linkedDocument->addLink(document);
                    break;
                }
            }
        }
    }
}
